#include <fstream>
#include <ios>
#include <iostream>
#include <string>
#include <algorithm>
#include <array>

#include <boost\filesystem.hpp>
#include <boost\multi_array.hpp>
#include <boost\lexical_cast.hpp>

#include "wingbox.h"


void wingbox::saveCommon(std::ostream& file) const
{
	
	file << getName() << ",,,Material,,,Stringer,,,Box\n";
	file << "Weight [kg]," << weight() << ",,density [kg/m3]," << b.getMaterial().density
		<< ",,width [mm]," << std::get<0>(top_stringers.front()).getWidth() * 1000 << ",,width [mm]," << b.getWidth() * 1000 << '\n';
	file << "Top stringers," << top_stringers.size() << ",,Young's modulus [MPa]," <<  b.getMaterial().youngsmodulus / 1e6
		<< ",,Thickness [mm]," << std::get<0>(top_stringers.front()).getThickness() * 1000 << ",,height [mm]," << b.getHeight() * 1000 << '\n';
	file << "Bottom stringers," << bottom_stringers.size() << ",,Yield stress [MPa]," <<  b.getMaterial().yieldstress / 1e6
		<< ",,,,,Length [mm]," << b.getLength() << '\n';
	file << "Rivets," << numberTotalRivets() << ",,Ultimate stress [MPa]," <<   b.getMaterial().ultimatestress / 1e6 << "\n\n";
	saveStringers(file);
	file << "\n\n";
	

}
void wingbox::saveStringers(std::ostream& file) const
{

	file << "Top Stringers," << getName() << '\n';
	file << "Name,x [mm],y [mm],length [mm],length from tip [mm]\n";
	std::for_each(top_stringers.begin(), top_stringers.end(), [this, &file] (const stringerlist::value_type& sdata ) {
		const stringer& s(std::get<0>(sdata));
		const position& p(std::get<1>(sdata));
		file << s.getName() << ',' << p.x * 1000 << ',' << p.y * 1000 << ',' << s.getLength() * 1000 << ',' << (p.x - s.getLength())*1000 << '\n';
	});
}
void wingbox::saveRivets(std::ostream& file) const
{
	file << "Rivets - wingbox," << getName() << '\n';
	file << "stringer,";
	std::for_each(top_stringers.begin(), top_stringers.end(), [this, &file] (const stringerlist::value_type& sdata ) {
		const stringer& s(std::get<0>(sdata));
		file << s.getName() << ',';
	});
	file << '\n';
	file << "y [mm],";
	std::for_each(top_stringers.begin(), top_stringers.end(), [this, &file] (const stringerlist::value_type& sdata ) {
		const position& p(std::get<1>(sdata));
		file << p.y * 1000 << ',';
	});
	file << "\nPositions [mm],\n";

	std::vector<double>::size_type maxnum(0);
	std::for_each(top_stringers.begin(), top_stringers.end(), [this, &maxnum] (const stringerlist::value_type& sdata ) {
		const std::vector<double>& v(std::get<2>(sdata));
		if (v.size() > maxnum) {
			maxnum = v.size();
		}
	});
	for (std::vector<double>::size_type n(0); n < maxnum; ++n) {
		file << ",";
		std::for_each(top_stringers.begin(), top_stringers.end(), [this, &file, &n] (const stringerlist::value_type& sdata ) {
			const std::vector<double>& v(std::get<2>(sdata));
			if  (n < v.size()) {
				file << v[n] * 1000;
			}
				
			file << ',';
		});
		file << '\n';
	}

}
void wingbox::saveBox(const std::string& filename) const
{

	boost::filesystem::path p(filename);
	auto path(p.parent_path());
	auto name(p.stem());
	auto ext(p.extension());
	if (ext.empty()) {
		ext = ".csv";
	}
	boost::filesystem::path top_filename(path /  (name.string() + "_top_plate" + ext.string()));
	boost::filesystem::path rivet_filename(path /  (name.string() + "_rivet_plate" + ext.string()));

	boost::filesystem::path failure_path(path /  (name.string() + "_failure" + ext.string()));

	saveCommon(std::fstream(top_filename.string(), std::ios::app));
	std::cout << "saved main data " << top_filename.string() << std::endl;
	saveRivets(std::fstream(rivet_filename.string(), std::ios::app));
	std::cout << "saved rivet data " << rivet_filename.string() << std::endl;
	saveFailure(std::fstream(failure_path.string(), std::ios::app), FailureSequenceIR(), FailureSequenceSheet(),
		FailureSequenceSheetAtRoot(), FailureSequenceBottom());
	std::cout << "saved failure data " << failure_path.string() << std::endl;

}

void wingbox::saveFailure(std::ostream& file, const FailSeqIRListTy& faillist, 
	const FailSeqSheetListTy& TopSheet, const FailSeqSheetListTy& TopSheetRoot, const std::pair<stringer_const_iterator, double>& failure) const
{
	saveFailureIR(file, faillist);
	saveFailureSheet(file, TopSheet, TopSheetRoot);
	saveFailureBot(file, failure);
}

void wingbox::saveFailureIR(std::ostream& file, const FailSeqIRListTy& faillist) const
{
	file << "Rivets Failure," << getName() << '\n';
	file << "stringer,";
	std::for_each(top_stringers.begin(), top_stringers.end(), [this, &file] (const stringerlist::value_type& sdata ) {
		const stringer& s(std::get<0>(sdata));
		file << s.getName() << ",,";
	});
	file << '\n';
	file << "y [mm],";
	std::for_each(top_stringers.begin(), top_stringers.end(), [this, &file] (const stringerlist::value_type& sdata ) {
		const position& p(std::get<1>(sdata));
		file << p.y * 1000 << ",,";
	});
	file << "\n,";
	std::for_each(top_stringers.begin(), top_stringers.end(), [this, &file] (const stringerlist::value_type& sdata ) {
		file << "pos [mm], Failure Load [N],";
	});
	file << "\n";

	std::vector<double>::size_type maxnum(0);
	std::for_each(top_stringers.begin(), top_stringers.end(), [this, &maxnum] (const stringerlist::value_type& sdata ) {
		const std::vector<double>& v(std::get<2>(sdata));
		if (v.size() > maxnum) {
			maxnum = v.size();
		}
	});
	std::vector<std::string> e(top_stringers.size() * 2,"");
	std::vector<std::vector<std::string> > data_arr(maxnum, e);

	for (stringerlist::size_type i(0); i < top_stringers.size(); ++i) {
		const auto& tvec(std::get<2>(top_stringers.at(i)));
		for (std::vector<double>::size_type j(0); j < tvec.size(); ++j) {
			data_arr.at(j).at(i*2) = boost::lexical_cast<std::string>(tvec[j] * 1000) ;
		}
	}

	for (FailSeqIRListTy::size_type i(0); i < faillist.size(); ++i) {
		
		auto p(std::distance(top_stringers.begin(), faillist.at(i).first));

		const auto& tvec(faillist.at(i).second);
		for (std::vector<double>::size_type j(0); j < tvec.size(); ++j) {
			auto& curval(data_arr.at(tvec[j].first).at(1+p*2));

			curval = boost::lexical_cast<std::string>(tvec[j].second);
		}
	}

	for (int i(0); i < data_arr.size(); ++i) {
		file << ",";
		const auto& t(data_arr[i]);
		for (int j(0); j < t.size(); ++j) {
			file << t[j];
			file << ',';
		}
		file << '\n';
	}
	file << '\n';
}
void wingbox::saveFailureSheet(std::ostream& file, const FailSeqSheetListTy& TopSheet, const FailSeqSheetListTy& TopSheetRoot) const
{
	if (!TopSheetRoot.empty()) {
		file << "Sheet Failure at root," << getName() << '\n';
		file << "Between stringer,";
	
		for (auto iter(TopSheetRoot.begin()); iter != TopSheetRoot.end(); ++iter) {
			const stringer& s(std::get<0>(*iter->first));
			const stringer& s2(std::get<0>(*(iter->first+1)));

			file << s.getName() << " & " << s2.getName() << ",";
		}
		file <<'\n';
		file << "x [mm],";
		for (auto iter(TopSheetRoot.begin()); iter != TopSheetRoot.end(); ++iter) {

			file << (std::get<1>(*iter->first).x) *1000<< ",";
		}
		file <<'\n';
		file << "y [mm],";
		for (auto iter(TopSheetRoot.begin()); iter != TopSheetRoot.end(); ++iter) {
			file << (std::get<1>(*iter->first).y + std::get<1>(*(iter->first+1)).y) *1000/2<< ",";
		}
		file <<'\n';
		file << "spacing [mm],";
		for (auto iter(TopSheetRoot.begin()); iter != TopSheetRoot.end(); ++iter) {
			file << std::abs(std::get<1>(*iter->first).y - std::get<1>(*(iter->first+1)).y) *1000<< ",";
		}
		file <<'\n';
		file << "Load [N],";
		for (auto iter(TopSheetRoot.begin()); iter != TopSheetRoot.end(); ++iter) {

			file << iter->second << ",";
		}
	}
	file << '\n';
	if (!TopSheet.empty()) {
		file << "\nSheet Failure at edge of each stringer," << getName() << '\n';
		file << "Between stringer,";
		for (auto iter(TopSheet.begin()); iter != TopSheet.end(); ++iter) {
			const stringer& s(std::get<0>(*iter->first));

			file << s.getName()  << ",";
		}
		file <<'\n';
		file << "x [mm],";
		for (auto iter(TopSheet.begin()); iter != TopSheet.end(); ++iter) {

			file << (std::get<1>(*iter->first).x - std::get<0>(*iter->first).getLength()) *1000<< ",";
		}
		file <<'\n';
		file << "y [mm],";
		for (auto iter(TopSheet.begin()); iter != TopSheet.end(); ++iter) {

			file << std::get<1>(*iter->first).y *1000<< ",";
		}
		file <<'\n';
		file << "spacing [mm],";
		for (auto iter(TopSheet.begin()); iter != TopSheet.end(); ++iter) {
			wingbox::stringer_const_iterator i(iter->first);
			file << std::abs(calcSpaceWithout(i, std::get<1>(*iter->first).x - std::get<0>(*iter->first).getLength() )) *1000<< ",";
		}
		file <<'\n';
		file << "Load [N],";
		for (auto iter(TopSheet.begin()); iter != TopSheet.end(); ++iter) {

			file << iter->second << ",";
		}
	}
	file << '\n';
	file << '\n';
}
void wingbox::saveFailureBot(std::ostream& file, const std::pair<stringer_const_iterator, double>& failure) const
{
	file << "Ultimate failure at root on bottom plate," << getName() << '\n';
	if (failure.first == top_stringers.end()) {
		file << "at wingbox edge\n";
		file << "x [mm]," << b.getLength() *1000<< '\n';
		file << "y [mm]," << b.getWidth()*1000 << '\n';
		file << "F [N]," << failure.second << '\n';
	} else {
		file << "around rivet hole\n";
		file << "x [mm]," << std::get<2>(*failure.first).front() * 1000 << '\n';
		file << "y [mm]," << std::get<1>(*failure.first).y *1000<< '\n';
		file << "F [N]," << failure.second << '\n';
	}
	file << '\n';

}